from simpledcpp.core.base import DCPPPart, DCPPEvent, start_new_thread, with_psyco
from simpledcpp.core.task import Task, Segment
from simpledcpp.core.handlers.LoadQueue import SaxHandler
from xml.sax import make_parser
import sys, os, time, shutil
from simpledcpp.core.connections.upload_connection import UploadConnection
from simpledcpp.core.connections.download_connection import DownloadConnection

class QueueManager(DCPPPart):
	""" Queue manager. """
	def __init__(self, parent):
		self.stopped = 0
		DCPPPart.__init__(self, parent)
		try:
			self.Events.Unsubscribe("ON_SYSTEM_SHUTDOWN", self.stop)
			self.Events.Subscribe("ON_NEED_UPLOAD", self._createUpload)
			self.Events.Subscribe("ON_NEED_DOWNLOAD", self._createDownload)
			self.Events.Subscribe("ON_SEARCH_RESULT", self._alternates)
			self.Events.Subscribe("GET_FILELIST", self._getfilelist)
			self.Events.Subscribe("GET_FILELIST_MD", self._getfilelistMD)
			self.Events.Subscribe("NEED_TASK", self._needTask)
			self.Events.Subscribe("REMOVE_TASK", self._removeTask)
		except:
			print _("Warning: QueueManager don't work correctly")
		self.queue = list()
		self._load()
		self._thread = start_new_thread(self.main_thread)
		self._thread2 = start_new_thread(self.auto_search_thread)

	def _removeTask(self, Event):
		try:
			self.queue.remove(Event.userdata[0])
			os.remove(Event.userdata[0].localfilename)
		except:
			pass

	def _getfilelist(self, Event):
		try:
			if Event.userdata[1] == self.settings.get("nickname"):
				self.Event(DCPPEvent("TRY_OWN_FILELIST"))
				return
			try:
				if Event.userdata[2] != None:
					METADATA = Event.userdata[2]
#					print METADATA
			except:
				METADATA = None
			t = Task(self, "files.xml.bz2", 0, "files.xml.bz2.%s"%Event.userdata[1], 10, METADATA = METADATA)
			t.AddSource(Event.userdata[1], Event.userdata[0])
			self.addTask(t)
		except:
			print sys.exc_info()

	def _getfilelistMD(self, Event):
		try:
			t = Task(self, "files.xml.bz2", 0, "files.xml.bz2.%s"%Event.userdata[1], 10, METADATA = "DOWNLOAD_DIR: %s %s"%(Event.userdata[1],Event.userdata[2]))
			t.AddSource(Event.userdata[1], Event.userdata[0])
			self.addTask(t)
		except:
			print sys.exc_info()

	@with_psyco
	def _needTask(self, Event):
		try:
			TTH, localfilename, filesize, nickname = Event.userdata[0], Event.userdata[1], Event.userdata[2], Event.userdata[3]
			try:
				METADATA = Event.userdata[4]
			except:
				METADATA = None
			t = Task(self, TTH, int(filesize), localfilename, METADATA = METADATA)
			if nickname != None:
				for nick in nickname.split("<{SPLITTER}>"):
					if nick != "":
						t.AddSource(nick)
			else:
				self.Event(DCPPEvent("SEARCH_TTH",(TTH,)))

			self.addTask(t)
		except:
			print sys.exc_info()

	def _alternates(self, Event):
		nickname = Event.userdata[0]
		TTH = Event.userdata[5]
		for task in self.queue:
			if task.TTH == TTH:
				task.AddSource(nickname)

	def _createDownload(self, Event):
		try:
			con = DownloadConnection(self, Event.userdata)
			self.Event(DCPPEvent("ON_CREATE_CONNECTION", (con,)))
		except:
			print sys.exc_info()
	def _createUpload(self, Event):
		con = UploadConnection(self, Event.userdata)
		self.Event(DCPPEvent("ON_CREATE_CONNECTION", (con,)))

	def isExists(self, subtask):
		for task in self.queue:
			if task.TTH == subtask.TTH and task.localfilename == subtask.localfilename:
				return True
		return False

	def addTask(self, task):
		task.localfilename = str(self.settings.get("unfinished")) + '/' + task.localfilename
		task.localfilename = task.localfilename.replace('//','/')
                if self.isExists(task):
                        self.Event(DCPPEvent("TASK_ALREADY_IN_QUEUE",(task,)))
                        del task
                        return

		try:
			file = open(task.localfilename, "w")
			file.close()
		except:
			pass
		if self._forcemkdir(task.localfilename):
			pass
		else:
			print _("Can't creating directory") + " %s"%task.localfilename
		self.Event(DCPPEvent("ON_NEW_TASK",(task,)))
		self.queue.append(task)
		# Request highest priority tasks.
		if task.priority == 10:
	                for source in task.sources.generatorFree():
        	                self.Event(DCPPEvent("ON_TASK_REQUEST",(task,source)))
                                time.sleep(0.2)

			

	def auto_search_thread(self):
		while True:
			time.sleep(int(self.settings.get("auto_search","100")))
			for i in self.queue:
				time.sleep(int(self.settings.get("auto_search","100")))
				self.Event(DCPPEvent("SEARCH_TTH",(i.TTH,)))

	def getTaskByTTH(self, TTH):
		for i in self.queue:
			if i.TTH == TTH:
				return i
		return None


	def _print(self, message,space = 0):
		for i in range(0,space):
			self._filedes.write(" ")
		self._filedes.write("%s\n"%message.replace("&","&amp;"))

	def stop(self):
		if not self.stopped:
			self.stopped = 1
			self._thread.kill()
			self._thread2.kill()
			self._save()

	def getGoal(self, Nickname):
		for task in self.queue:
			for source in task.sources.generatorFree():
				if source.nickname == Nickname:
					if task.segment.hasFree() and task.RequestSource(source.nickname, source.hubname):
						return task
		return None

	def retGoal(self, Nickname):
		for task in self.queue:
			for source in task.sources.generator():
				if not source.is_free() and source.nickname == Nickname:
					task.FreeSource(Nickname)
					try:
						self._checkAlready(task)
					except:
						pass
					return True
		return False
		
	def _checkAlready(self, task):
		if task.segment.percent() == 100.0:
			self._AlreadyDownload(task)
	
	def _forcemkdir(self, filename):
		cd = "/"
		try:
			for i in filename.split('/')[:-1]:
				if not os.path.isdir(cd + i + '/') and i != "":
					try:
						os.mkdir(cd + i + '/')
					except:
						pass
				cd = cd + i + '/'
			return True
		except:
			return False

	def _movefile(self, task):
                if os.path.isfile(task.localfilename):
                        fname = task.localfilename.replace(self.settings.get('unfinished') + '/', '')
			moveto = None
			try:
				if task.METADATA != None:
					if task.METADATA.startswith('DOWNLOADTO:'):
						if os.path.isdir(task.METADATA.split(':')[1]):
							moveto = task.METADATA.split(':')[1] + '/' + fname
			except:
				pass
			if moveto == None:
	                        moveto = self.settings.get('downloads') + '/' + fname
			self._forcemkdir(moveto)
                        shutil.move(task.localfilename, moveto)
			task.localfilename = moveto


	def _getHash(self, filename):
                try:
                	self.__temp = '/tmp'
		        if os.getenv('TEMP') != None:
				if os.path.isdir(os.getenv('TEMP')):
					self.__temp = os.getenv('TEMP')
                        tmp = None
                        try:
                                os.system('tthsum "%s" > %s/tthsum.tmp 2>/dev/null' % (filename, self.__temp))
                        except:
                                print "Here"
                        hashf = open('%s/tthsum.tmp'%self.__temp)
                        hashl = hashf.readlines()
                        if hashl != []:
                                hashl = hashl[0].strip()
                                tmp = hashl.split('  ')
                                tmp = tmp[0]
                        hashf.close()
                        os.remove('%s/tthsum.tmp'%self.__temp)
                        return tmp
                except:
                        return None

	def _AlreadyDownload(self, task):
		self.queue.remove(task)
		self.Event(DCPPEvent("ON_REMOVE_TASK",(task,)))
		# If check_tth == 1, check tth
		if int(self.settings.get("check_tth",0)) == 1:
			hash = self._getHash(task.localfilename)
			if hash == task.TTH or len(task.TTH) < 39:
	        	        self._movefile(task)
				self.Event(DCPPEvent("ON_ALREADY_DOWNLOAD", (task,)))
			else:
				self.Event(DCPPEvent("CORRUPTED_FILE", (task,)))
				os.remove(task.localfilename)
		else:
			self._movefile(task)
			self.Event(DCPPEvent("ON_ALREADY_DOWNLOAD", (task,)))
		if len(self.queue) == 0:
			self.Event(DCPPEvent("QUEUE_IS_EMPTY"))

	def main_thread(self):
		while True:
			time.sleep(5)
			try:
				self._processing()
			except:
				pass

	def _TasksWithPriority(self, priority):
		for i in self.queue:
			if int(i.priority) == priority:
				yield i

	def _processing(self):
		for task in self.queue:
			self._checkAlready(task)
		for priority in range(10, -1, -1):
#			time.sleep(2)
			for task in self._TasksWithPriority(priority):
				if task.segment.hasFree():
					for source in task.sources.generatorFree():
						self.Event(DCPPEvent("ON_TASK_REQUEST",(task,source)))
#						print "Requested %s."%(priority)
						time.sleep(0.1)

	def _clear(self):
		self.queue = list()

	@with_psyco
	def _load(self, filename = None):
		if filename == None:
			filename = os.path.join(os.getenv("HOME"), ".simpledcpp/queue.xml")
		if os.path.isfile(filename):
			parser = make_parser()
			curHandler = SaxHandler()
			parser.setContentHandler(curHandler)
			try:
				parser.parse(open(filename))
			except IOError:
				print _("Error while loading")," %s"%filename
			for i in curHandler.tasks:
				try:
					if i['filesize'] == 'None':
						filesize = None
					else:
						filesize = int(i['filesize'])

					b = Task(self, None, None, None, None, False)
					b.filesize = filesize
					b.TTH = i['TTH']
					if i['METADATA'] == 'None':
						i['METADATA'] = None
					b.METADATA = i['METADATA']
					b.localfilename = i['localfilename']
					b.priority = i['priority']
					for source in i['sources']:
						nick,hub = source.split(':',1)
						if hub == 'None':
							hub = None
						b.AddSource(nick, hub)
					b.segment = Segment(b, b.filesize, False)
					for segment in i['segments']:
						b.segment.segments[segment[0]] = (0, int(segment[1]), int(segment[2]))
					self.queue.append(b)
				except:
					print _("Error while loading Task.")
					print sys.exc_info()

	@with_psyco		
	def _save(self, filename = None):
		if filename == None:
			filename = os.path.join(os.getenv("HOME"), ".simpledcpp/queue.xml")
		self._filedes = open(filename,"w")
		self._print("<Queue>")
		for i in self.queue:
			self._print("<Task>",2)
			self._print('<p name="TTH">%s</p>'%i.TTH,4)
			self._print('<p name="filesize">%s</p>'%i.filesize,4)
			self._print('<p name="localfilename">%s</p>'%i.localfilename,4)
			self._print('<p name="priority">%s</p>'%i.priority,4)
			self._print('<p name="METADATA">%s</p>'%i.METADATA,4)
			self._print('<Sources>',4)
			for k in i.sources.generator():
				self._print('<p name="source">%s:%s</p>'%(k.nickname.replace("&","&amp;"),k.hubname),6)
			self._print('</Sources>',4)
			self._print("<Segments>",4)
			for k in i.segment.segments:
				self._print('<segment from="%s" to="%s">%s</segment>'%(i.segment.segments[k][1], i.segment.segments[k][2],k),4)
			self._print("</Segments>",4)
			self._print("</Task>",2)
		self._print("</Queue>")
		self._filedes.close()
		print _("Queue saved.")

